#include "gameview.h"
#include "mainwindow.h"
#include "gfxitems/gfxTowerBeam.h"
#include "gfxitems/gfxTowerBeamFireFX.h"
#include "gfxitems/gfxUnit.h"
#include "gfxitems/gfxUnitEmitter.h"
#include "gfxitems/gfxUnitDeathFX.h"
#include "gfxitems/gfxDamageTextFX.h"
#include <QDebug>
#include <QMouseEvent>
#include <QRegion>


GameView::GameView(QWidget *parent)
   : QGraphicsView(parent),
   mMainWindow(0),
   mCursorItem(0)
{
   mShowRanges = false;
   mBlendRanges = false;

   setMouseTracking(true);
   setScene(&mScene);
   centerOn(90, 50);
   clearCursor();
}


GameView::~GameView()
{
   qDebug() << "~GameView";
}


void GameView::setMainWindow(MainWindow *mainWindow)
{
   mMainWindow = mainWindow;
}


void GameView::setGame(ClientGame *game)
{
   mGame = game;
}


void GameView::mouseDoubleClickEvent(QMouseEvent *event)
{

}


void GameView::mousePressEvent(QMouseEvent *event)
{
   if (event->button() == Qt::LeftButton)
   {
      if (mCursorItem)
      {
         ENTITY_TYPE type = mCursorItem->stats().type;
         if (type == ENTITY_UNIT)
         {
            // Hide so itemAt doesn't return mCursorItem
            mCursorItem->setVisible(false);
            GFXItem *clickedGFX = dynamic_cast<GFXItem *>(itemAt(event->pos()));
            mCursorItem->setVisible(true);

            if (clickedGFX && clickedGFX->entity() && clickedGFX->entity()->base.type==ENTITY_UNITEMITTER)
            {
               // Check if emitter queue is full? can be done server-side worst case
               if ((quint32)clickedGFX->entity()->emitterContents.size() < clickedGFX->entity()->emitterCapacity)
                  emit placedEntity(type, mapToScene(event->pos()), clickedGFX->entity()->id);
            }
         }
         else if (type == ENTITY_TOWER)
         {
            mCursorItem->setVisible(false);
            QList<QGraphicsItem *> collidingItems = mScene.collidingItems(mCursorItem, Qt::IntersectsItemShape);
            mCursorItem->setVisible(true);

            if (collidingItems.isEmpty())
               emit placedEntity(type, mapToScene(event->pos()), 0);
         }
      }
      else // No cursor item
      {
         GFXItem *clickedGFX = dynamic_cast<GFXItem*>(itemAt(event->pos()));
         if (clickedGFX && clickedGFX->entity())
         {
            if (clickedGFX->entity()->base.type == ENTITY_UNIT)
               setSelection(clickedGFX->entity()->squadID, clickedGFX->entity()->base.type);
            else
               setSelection(clickedGFX->entity()->id, clickedGFX->entity()->base.type);
         }
         else
         {
            setSelection(0);
            setDragMode(QGraphicsView::ScrollHandDrag);
            QGraphicsView::mousePressEvent(event);
         }
      }
   }
   else if (event->button() == Qt::RightButton)
   {
      //if (mCursorItem) mCursorItem->setVisible(false);

      //if (itemAt(event->pos()))
      //{
      //   QGraphicsView::mousePressEvent(event);
      //}
      //else
      //{

         // This is a hack to basically fake the graphicsview into
         // HandDrag mode on a right click instead of left click
         Qt::MouseButton mb = Qt::LeftButton;
         QMouseEvent e(event->type(), event->pos(), event->globalPos(), Qt::LeftButton, mb, event->modifiers());
         setDragMode(QGraphicsView::ScrollHandDrag);
         QGraphicsView::mousePressEvent(&e);
      //}
      //if (mCursorItem) mCursorItem->setVisible(true);
   }
   else if (event->button() == Qt::MiddleButton)
   {
      setSelection(0);
   }

}


void GameView::mouseReleaseEvent(QMouseEvent *event)
{
   //QGraphicsView::mouseReleaseEvent(event); // fucks up selection
   setDragMode(QGraphicsView::NoDrag);
}


void GameView::leaveEvent(QEvent *event)
{
   QGraphicsView::leaveEvent(event);

   if (mCursorItem)
      mCursorItem->setVisible(false);
}


void GameView::mouseMoveEvent(QMouseEvent *event)
{
   QGraphicsView::mouseMoveEvent(event);
   if (mCursorItem)
   {
      mCursorItem->setVisible(true);
      mCursorItem->setPos(mapToScene(event->pos()));
   }
}

void GameView::wheelEvent(QWheelEvent *event)
{
   /*
   // Some fancy math to get the mouseWheel scroll/pan to work the way it does.
   // I should probably 'splain this a little at some point. And it still ain't
   // workin quite right...
   qreal panSpeedLimiter = width()*0.14;
   QPointF sceneCenterS = mapToScene(rect().center());
   QPointF eventPosS = mapToScene(event->pos());
   //QPointF sceneCenterWindow = mapToGlobal(rect().center());
   QPointF sceneCenterWindowG = mapToGlobal(rect().center());
   QPointF eventPosG = mapToGlobal(event->pos());
   QPointF windowCenterDelta = eventPosG - sceneCenterWindowG;

   //qDebug() << "SCENE: sccw=" << sceneCenterWindowG << "ep=" << eventPosG << "dc=" << windowCenterDelta;

   // Cap panning so the screen edge doesn't send us flying off into nowhere
   if (windowCenterDelta.x() > panSpeedLimiter)
      windowCenterDelta.setX(panSpeedLimiter);
   else if (windowCenterDelta.x() < -panSpeedLimiter)
      windowCenterDelta.setX(-panSpeedLimiter);
   if (windowCenterDelta.y() > panSpeedLimiter)
      windowCenterDelta.setY(panSpeedLimiter);
   else if (windowCenterDelta.y() < -panSpeedLimiter)
      windowCenterDelta.setY(-panSpeedLimiter);

   // Left pan doesn't seem to work all that well, same with up pan. Weird.
   //if (windowCenterDelta.x() < 0.0 && (event->delta() > 0))
   //windowCenterDelta.setX(windowCenterDelta.x()*1.5);

   qreal horizontalScale = windowCenterDelta.x() / (width()/2) * mZoomLevel;
   qreal verticalScale = windowCenterDelta.y() / (height()/2) * mZoomLevel;
   qreal newX = (eventPosS.x() - sceneCenterS.x()) * qAbs(horizontalScale);
   qreal newY = (eventPosS.y() - sceneCenterS.y()) * qAbs(verticalScale);

   //qDebug() << "SCENE: AFTER dc=" << windowCenterDelta
   //    << "nx=" << newX
   //    << "ny=" << newY
   //    << "pl=" << panSpeedLimiter;

   // Determine new center to pan the map as we zoom
   QPointF newCenter((sceneCenterS.x() + newX), (sceneCenterS.y() + newY));
*/
   // Apply pan and zoom
   if (event->delta() > 0)
      zoom(1);
   else
      zoom(0);
   //centerOn(newCenter);

   // Suppresses vert/horiz scrolling
   event->accept();
}


void GameView::zoom(bool zoomIn)
{
   if (zoomIn)
   {
      scale(1.2, 1.2);
      mZoomLevel*=0.97;
   }
   else
   {
      scale(1/1.2, 1/1.2);
      mZoomLevel*=1.03;
   }
}

void GameView::clearCursor()
{
   //qDebug() << "GameView::clearCursor";
   if (mCursorItem)
   {
      mScene.removeItem(mCursorItem);
      delete mCursorItem;
      mCursorItem = 0;
   }
   setDragMode(QGraphicsView::ScrollHandDrag);
}


void GameView::updateCursor(const EntityBaseStats &stats, const qint32 numberIndex)
{
   clearCursor();

   switch (stats.type)
   {
   case ENTITY_UNIT:
      mCursorItem = new GFXUnit(stats);
   break;
   case ENTITY_TOWER:
      mCursorItem = new GFXTowerBeam(stats);
      mCursorItem->setShowRange(true);
      mCursorItem->setBlendRange(mBlendRanges);
   break;
   default:
   break;
   }

   if (mCursorItem)
   {
      mCursorItem->setFlag(QGraphicsItem::ItemIsSelectable, false);
      mCursorItem->setVisible(false);
      mCursorItem->setPulsate(true);
      mCursorItem->setSquadSize(numberIndex);
      mScene.addItem(mCursorItem);
      setDragMode(QGraphicsView::NoDrag);
   }
}

void GameView::tick()
{
   //qDebug() << "GameView::tick";
   foreach(GFXItem *gfx, mEntityGfx)
   {
      gfx->tick();
   }
   foreach(GFXItem *fx, mFx)
   {
      fx->tick();
      if (!fx->active())
      {
         //qDebug() << "GameView::tick removing fx";
         mScene.removeItem(fx);
         mFx.removeOne(fx);
         delete fx;
      }
   }
   if (mCursorItem)
      mCursorItem->tick();
}


void GameView::addEntity(ClientEntity *entity)
{
   if (!mEntityGfx.contains(entity->id))
   {
      GFXItem *gfx = 0;

      switch (entity->base.type)
      {
      case ENTITY_UNIT: gfx = new GFXUnit(entity); break;
      case ENTITY_TOWER: gfx = new GFXTowerBeam(entity); break;
      case ENTITY_UNITEMITTER: gfx = new GFXUnitEmitter(entity); break;
      default: break;
      }

      if (gfx)
      {
         mEntityGfx[entity->id] = gfx;
         mScene.addItem(gfx);
         gfx->setPos(entity->pos.toPointF());
         gfx->setShowRange(mShowRanges);
         gfx->setBlendRange(mBlendRanges);
         qDebug() << "GameView::addEntity gfx added for id=" << entity->id << "at" << entity->pos;
      }
      else
      {
         qDebug() << "GameView::addEntity ERROR no gfx added for id=" << entity->id;
      }

   }
   else // ID in use
   {
      qDebug() << "GameView::addEntity ERROR id=" << entity->id << "already in use";
   }
}


void GameView::removeEntity(int id)
{
   qDebug() << "GameView::removeEntity id=" << id;
   GFXItem *item = mEntityGfx[id];
   mScene.removeItem(item);
   mEntityGfx.remove(id);
   delete item;
}


void GameView::addFX(FX_TYPE type, ClientEntity *entity, qreal value)
{
   GFXItem *gfx = 0;

   switch (type)
   {
   case FX_TOWER_BEAM_FIRE: gfx = new GFXTowerBeamFireFX(entity); break;
   case FX_UNIT_DEATH: gfx = new GFXUnitDeathFX(entity); break;
   case FX_DAMAGE_TEXT: gfx = new GFXDamageTextFX((qint32)value, entity); break;
   default: break;
   }

   if (gfx)
   {
      mFx.append(gfx);
      mScene.addItem(gfx);
      if (entity)
         gfx->setPos(entity->pos.toPointF());
      //qDebug() << "GameView::addEntity fx added";
   }
}


void GameView::drawPaths(const PathListType &paths)
{
   if (!paths.isEmpty())
   {
      // Draw thick whie lines over paths
      PathListType::const_iterator it = paths.begin();
      while (it != paths.end())
      {
         for (int j=1; j<it.value().size(); ++j)
         {
            mScene.addLine(it.value().at(j-1).x(), it.value().at(j-1).y(),
                           it.value().at(j).x(), it.value().at(j).y(),
                           QPen(Qt::white,35));
         }
         ++it;
      }

      it = paths.begin();
      while (it != paths.end())
      {
         for (int j=1; j<it.value().size(); ++j)
         {
            mScene.addLine(it.value().at(j-1).x(), it.value().at(j-1).y(),
                           it.value().at(j).x(), it.value().at(j).y(),
                           QPen(Qt::black,30));
         }
         ++it;
      }
   }
}


void GameView::setShowRanges(bool value)
{
   mShowRanges = value;
   foreach(GFXItem *gfx, mEntityGfx)
      gfx->setShowRange(mShowRanges);
}

void GameView::setBlendRanges(bool value)
{
   mBlendRanges = value;
   foreach(GFXItem *gfx, mEntityGfx)
      gfx->setBlendRange(mBlendRanges);
}

void GameView::setSelection(const quint32 id, const ENTITY_TYPE type)
{
   qDebug() << "GameView::setSelection=" << id << "old=" << mSelectedID << "type=" << type;

   if (id == 0 || mSelectedID != id)
   {
      clearCursor();
      mSelectedID = id;

      foreach (GFXItem *gfx, mEntityGfx)
      {
         if (gfx->entity())
         {
            if (type == ENTITY_UNIT) // select squad
            {
               gfx->setSelected((gfx->entity()->squadID==mSelectedID));
               gfx->setShowRange((gfx->entity()->squadID==mSelectedID)||mShowRanges);
            }
            else
            {
               gfx->setSelected((gfx->entity()->id==mSelectedID));
               gfx->setShowRange((gfx->entity()->id==mSelectedID)||mShowRanges);
            }
         }
      }
   }
   emit newSelection(type, mSelectedID);
}
